/*
 * Broadcast.cpp
 *
 *  Created on: 08/ott/2013
 *      Author: alessandrob
 */
#include<iostream>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<opencv2/opencv.hpp>
#include<opencv2/features2d/features2d.hpp>
#include<opencv2/highgui/highgui.hpp>
#include "Utility.h";
#define BUFLEN 1400
#define PORT 5002
#define PORTB 5003
using namespace std;
using namespace cv;

void broadcast(int threshold)
{
	cout<<"Setting up BRISK..."<<endl;
	struct sockaddr_in server_address,broadcast_address,my_address;
	char ready[5];
	char ack[30];
	char sync[30];
	int sockfd,dataReceived,cols,rows;
	int blocks,receivedBlocks=0,CPUspeed = 5000000;
	Mat picture;
	bool finished = false;
	double det_time;
	unsigned int slen=sizeof(server_address);
	//unsigned int slenb=sizeof(broadcastPermission);
	Mat descriptors;
	vector<KeyPoint> kp;
	vector<int> params = vector<int>(2);
	params[0]=0;

	//Setting up BRISK and first computation
	Ptr<FeatureDetector> detector;
	detector = new BRISK(threshold,4);
	Ptr<DescriptorExtractor> descriptorExtractor;
	descriptorExtractor = new BRISK(threshold,4);
	Mat test = imread("/home/ubuntu/test.jpg",0);
	detector->detect(test,kp);
	descriptorExtractor->compute(test,kp,descriptors);
	cout<<"Testing keypoints found: "<<kp.size()<<endl;

	//UDP Socket
	cout<<"Setting up UDP client..."<<endl;
	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0))==-1)
		printErrorLog("Client socket not created correctly.");
	/*if ((sockBroadcast = socket(AF_INET, SOCK_DGRAM, 0))==-1)
		printErrorLog("Client socket not created correctly.");
	if(setsockopt(sockBroadcast,SOL_SOCKET,SO_BROADCAST,&broadcastPermission,sizeof(broadcastPermission))<0)
		printErrorLog("Setting socket option error ");*/
	bzero(&my_address, sizeof(my_address));
	my_address.sin_family = AF_INET;
	//Listening on 5002
	my_address.sin_port = htons(PORT);
	my_address.sin_addr.s_addr = htonl(INADDR_BROADCAST);
	if (bind(sockfd, (struct sockaddr *)&my_address, sizeof(my_address))==-1)
		printErrorLog("Wrong binding. The client is NOT running");
	bzero(&server_address, sizeof(server_address));
	server_address.sin_family = AF_INET;
	server_address.sin_port = htons(PORT);
	char ip[16];
	memmove(ip,"10.42.0.5",sizeof(ip));
	if(inet_aton(ip,&server_address.sin_addr)==0)
		printErrorLog("IP address not correct");
	cout<<"Ready for the connection..."<<endl;
	ready[0]='r';
	ready[1]= (CPUspeed >> 24);
	ready[2]= (CPUspeed >> 16);
	ready[3]= (CPUspeed >> 8);
	ready[4]= (CPUspeed);
	ack[0]='p';
	for(int i=1;i<30;i++)
		ack[i]=0;
	if (sendto(sockfd, ready, sizeof(char)*5, 0, (struct sockaddr*)&server_address, slen)==-1)
		printErrorLog("Connection request failed");
	cout<<"Connection done. Ready to get synchronized..."<<endl;
	/*bzero(&broadcast_address, sizeof(broadcast_address));
	broadcast_address.sin_family = AF_INET;
	broadcast_address.sin_port = htons(PORTB);*/
	//broadcast_address.sin_addr.s_addr = ine("10.42.0.5");
	dataReceived = recvfrom(sockfd,&sync[0],sizeof(char)*30,0,(struct sockaddr*)&broadcast_address,&slen);
	cout<<"Sync ACK length: "<<dataReceived<<endl;
	printf("Byte read: %d\n",sync[0]);
	if(dataReceived!=sizeof(char)*30)
		printErrorLog("Synchronization failed");
	if(sync[0]=='s')
	{
		cout<<"Got a sync ACK from: "<<inet_ntoa(broadcast_address.sin_addr)<<endl;
		//Synchronization
		cout<<"Synchronization done. Timer started"<<endl;
		det_time=(double)getTickCount();
		//RECEIVING PACKETS
		//Receive the number ok blocks
		uchar infoPacket[12];
		if(recvfrom(sockfd,&infoPacket[0],sizeof(char)*12,0,(struct sockaddr*)&server_address,&slen)==-1)
			printErrorLog("Information packet not received");
		cout<<"Information packet received"<<endl;
		cols = (infoPacket[0]<<24);
		cols = cols+(infoPacket[1]<<16);
		cols = cols+(infoPacket[2]<<8);
		cols = cols+(infoPacket[3]);
		rows = (infoPacket[4]<<24);
		rows = rows+(infoPacket[5]<<16);
		rows = rows+(infoPacket[6]<<8);
		rows = rows+(infoPacket[7]);
		blocks = (infoPacket[8]<<24);
		blocks = blocks+(infoPacket[9]<<16);
		blocks = blocks+(infoPacket[10]<<8);
		blocks = blocks+(infoPacket[11]);
		/*if(recvfrom(sockfd,&cols,sizeof(int),0,(struct sockaddr*)&server_address,&slen)==-1)
			printErrorLog("Columns not received");*/
		cout<<"Columns: "<<cols<<endl;
		/*if(recvfrom(sockfd,&rows,sizeof(int),0,(struct sockaddr*)&server_address,&slen)==-1)
			printErrorLog("Rows not received");*/
		cout<<"Rows: "<<rows<<endl;
		/*if(recvfrom(sockfd,&blocks,sizeof(int),0,(struct sockaddr*)&server_address,&slen)==-1)
			printErrorLog("Number of blocks not received");*/
		cout<<"Blocks: "<<blocks<<endl;
		int bytesPerPacket = (int)(cols*rows)/blocks;
		int lastPacket = (cols*rows)-(bytesPerPacket*blocks);
		cout<<"Bytes per packet: "<<bytesPerPacket<<endl;
		cout<<"Last packet: "<<lastPacket<<endl;
		broadcast_packet packet[blocks+1];
		uchar buffer[bytesPerPacket+8];
		//Receiving data
		double start_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Start receiving packets time: T+"<<start_time*1000<<"ms"<<endl;
		while(!finished)
		{
			dataReceived=recvfrom(sockfd,&buffer[0],bytesPerPacket+8,0,(struct sockaddr*)&broadcast_address,&slen);
			if(dataReceived == 3 && buffer[0]=='e' && buffer[1]=='n' && buffer[2]=='d')
				finished = true;
			else if(dataReceived==(lastPacket+8))
			{
				for(int k=0;k<(lastPacket+8);k++)
					packet[receivedBlocks].serializedPacket[k]=buffer[k];
				receivedBlocks++;
			}
			else if(dataReceived==(bytesPerPacket+8))
			{
				for(int k=0;k<(bytesPerPacket+8);k++)
					packet[receivedBlocks].serializedPacket[k]=buffer[k];
				receivedBlocks++;
			}
		}
		server_address.sin_port = htons(PORT);
		if (sendto(sockfd, ack, sizeof(char)*30, 0, (struct sockaddr*)&server_address, slen)!=sizeof(char)*30)
			printErrorLog("Connection request failed");
		cout<<"Blocks received: "<<receivedBlocks<<"/"<<blocks<<endl;
		double rec_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Receiving time: T+"<<rec_time*1000<<"ms"<<endl;
		//Ordering packets (bubble sort)
		orderPackets(packet,receivedBlocks);
		//Deserializing packets
		deserializePackets(packet,receivedBlocks);
		double d_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Deserializing packet time: T+"<<d_time*1000<<"ms"<<endl;
		//Rebuilding the image
		if(lastPacket>0)
			blocks = blocks + 1;
		//Building image
		Mat picture;
		uchar data[cols*rows];
		int packetNumber = 1,offset=0;
		for(int i=0;packetNumber<(blocks+1);packetNumber++)
		{
			if(packet[i].packetNumber == packetNumber)
			{
				if(packetNumber == blocks && lastPacket>0)
				{
					//normal last packet
					for(int k=0;k<lastPacket;k++)
						data[offset+k]=packet[i].serializedPacket[k+8];
					offset = offset + lastPacket;
					i++;
				}
				else
				{
					//normal packet
					for(int k=0;k<bytesPerPacket;k++)
						data[offset+k]=packet[i].serializedPacket[k+8];
					offset = offset + bytesPerPacket;
					i++;
				}
			}
			else
			{
				//missing packet
				for(int k=0;k<bytesPerPacket;k++)
					data[offset+k]=125;
				offset = offset + bytesPerPacket;
			}
		}
		cout<<"Offset: "<<offset<<endl;
		picture = Mat(rows,cols,CV_8UC1,data,cols);
		//imwrite("/home/ubuntu/broadcastKeypoints.jpg",picture,params);
		//picture = buildImage(packet,blocks,cols,rows,bytesPerPacket,lastPacket);
		double b_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Building picture time: T+"<<b_time*1000<<"ms"<<endl;
		//Processing
		Mat broadcastImage;
		detector->detect(picture,kp);
		descriptorExtractor->compute(picture,kp,descriptors);
		double p_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Client processing time: T+"<<p_time*1000<<"ms"<<endl;
		//drawKeypoints(picture,kp,broadcastImage);
		//imwrite("/home/ubuntu/broadcastKeypoints.jpg",broadcastImage,params);
		cout<<"Keypoints found: "<<kp.size()<<endl;
		close(sockfd);
		//imwrite("/home/ubuntu/broadcastImage.jpg",picture,params);
		//Sending kps and descriptors
		cout<<"SENDING DATA TO THE SERVER"<<endl;
		//opening TCP socket
		if ((sockfd = socket(AF_INET, SOCK_STREAM, 0))==-1)
			printErrorLog("Client socket not created correctly.");
		cout<<"Client TCP socket created correctly."<<endl;
		bzero(&server_address, sizeof(server_address));
		server_address.sin_family = AF_INET;
		server_address.sin_port = htons(PORT);
		char ip[16];
		memmove(ip,"10.42.0.5",sizeof(ip));
		if(inet_aton(ip,&server_address.sin_addr)==0)
			printErrorLog("IP address not correct");
		cout<<"Ready for the connection..."<<endl;
		if(connect(sockfd,(const struct sockaddr*)&server_address,sizeof(server_address))<0)
			printErrorLog("Connection refused");
		sendData(sockfd,p_time*1000,kp.size(),kp,descriptors);
		/*for(int i=0;i<kp.size();i++)
		{
			printf("angle: %f x: %f y: %f size: %f\n",kp[i].angle,kp[i].pt.x,kp[i].pt.y,kp[i].size);
		}*/
	}
}


